package com.note.gateway.config;

import cn.hutool.core.text.AntPathMatcher;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.note.config.constants.constants.GatewayConstants;
import com.note.gateway.constants.ResponseCodeConstants;
import com.note.gateway.domain.User;
import com.note.gateway.dto.UserDto;
import com.note.gateway.enums.JwtErrorCode;
import com.note.gateway.service.UserService;
import com.note.gateway.util.JwtUtil;
import com.note.gateway.util.RedisUtils;
import com.note.gateway.util.ResponseVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.util.Objects;

/**
 * @Package：com.note.user.config
 * @Name：InterceptorConfig
 * @Author：热伊木
 * @Email：uyevan@163.com
 * @Date：2023-12-12-17:01
 * @Description：网关中心拦截器配置 --- 已使用第一种方式
 * 第二种方式：实现GlobalFilter接口来实现拦截验证 ---> 适合多模块应用；
 */

@Slf4j
@Component
public class InterceptorFilterConfig implements GlobalFilter, Ordered {
    /*hutool字符匹配*/
    private final AntPathMatcher matcher = new AntPathMatcher();

    /*@Lazy*//*延迟初始化 第二种方法：开启循环引用*/
    @Autowired
    private UserService userService;

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        /*添加验证key 防止下游直接访问接口*/
        ServerHttpRequest.Builder builder = request.mutate();
        builder.header(GatewayConstants.GATEWAY_KEY_NAME, GatewayConstants.GATEWAY_KEY);

        /*匹配规则验证*/
        if (pathPatterns(request, GatewayConstants.DONT_PATH_PATTERNS)) {
            /*无需验证*/
            return chain.filter(exchange);
        }

        //获取token
        String token = request.getHeaders().getFirst(JwtUtil.AUTHORIZATION);
        ResponseVo responseVo = new ResponseVo(ResponseCodeConstants.ERROR, false, "", null);
        if (!StringUtils.hasLength(token)) {
            responseVo.setMessage(JwtErrorCode.ERROR_TOKEN_EMPTY.getMessage());
            return writerPrintln(responseVo, response);
        }
        /*获取账号和密码*/
        String userName = JwtUtil.getPayloadFromToken(token, "username");
        String passWord = JwtUtil.getPayloadFromToken(token, "password");
        /*如果没有问题则判断Redis中的状态*/
        if (!RedisUtils.exists(userName) || !RedisUtils.getValue(userName).equals(token)) {
            responseVo.setMessage(JwtErrorCode.ERROR_DONT_EXITS_TOKEN.getMessage());
            return writerPrintln(responseVo, response);
        }

        /*用户数据验证（是否存在 | 密码状态）*/
        if (pathPatterns(request, GatewayConstants.ADD_PATH_PATTERNS)) {
            /*Get请求检验令牌内的账号和提交的账号是否一样*/
            if (request.getMethod() == HttpMethod.GET) {
                if (!Objects.equals(request.getQueryParams().getFirst("username"), userName)) {
                    //token和账号不匹配
                    responseVo.setMessage(JwtErrorCode.TOKEN_USER_MISMATCH.getMessage());
                    return writerPrintln(responseVo, response);
                }
            }
            /*需要用户名*/
            UserDto userDto = getUserDto(request, userName, passWord);
            User user = userService.userInfo(userDto);
            if (user == null) {
                //账号不存在
                responseVo.setMessage(JwtErrorCode.USER_NOT_FOUND.getMessage());
                return writerPrintln(responseVo, response);
            }
            if (!user.getPassword().equals(passWord)) {
                //密码不正确
                responseVo.setMessage(JwtErrorCode.WRONG_PASSWORD.getMessage());
                writerPrintln(responseVo, response);
            }
        }

        try {
            // 验证令牌（截取前缀）
            String replace = token.replace(JwtUtil.TOKEN_PREFIX, "");
            /*如果存在任何问题就抛出异常拦截请求*/
            JwtUtil.verify(replace);
            return chain.filter(exchange);
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            responseVo.setMessage(JwtErrorCode.ERROR_SIGNATURE_MISMATCH.getMessage());
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            responseVo.setMessage(JwtErrorCode.ERROR_TOKEN_EXPIRED.getMessage());
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            responseVo.setMessage(JwtErrorCode.ERROR_ALGORITHM_MISMATCH.getMessage());
        } catch (InvalidClaimException e) {
            e.printStackTrace();
            responseVo.setMessage(JwtErrorCode.ERROR_PAYLOAD_INVALID.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            responseVo.setMessage(JwtErrorCode.ERROR_INVALID_TOKEN.getMessage());
        }
        return writerPrintln(responseVo, response);
    }

    /*获取用户信息*/
    private static UserDto getUserDto(ServerHttpRequest request, String userName, String passWord) {
        UserDto userDto = new UserDto();
        //用户名获取
        String requestUsername = null;
        if (request.getMethod() == HttpMethod.GET) {
            /*获取Query参数*/
            MultiValueMap<String, String> valueMap = request.getQueryParams();
            requestUsername = valueMap.getFirst("username");
            if (!StringUtils.hasLength(requestUsername)) {
                requestUsername = userName;
            }
        }
        /*如果是POST或其他请求*/
        else {
            /*POST或其他请求实体令牌账号*/
            requestUsername = userName;
        }

        userDto.setUsername(requestUsername);
        userDto.setPassword(passWord);
        return userDto;
    }

    /*结果输出*/
    private Mono<Void> writerPrintln(ResponseVo responseVo, ServerHttpResponse response) throws IOException {
        /*这里在返回头添加编码，否则中文会乱码*/
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] bytes = JSON.toJSONBytes(responseVo, SerializerFeature.WriteMapNullValue);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }

    /*路径匹配*/
    private boolean pathPatterns(ServerHttpRequest request, String[] paths) {
        String path = request.getURI().getPath();
        //log.error("gatewayPath:{}", path);
        /*无需拦截*/
        boolean isMatcher = false;
        for (String pattern : paths) {
            if (matcher.match(pattern, path)) {
                isMatcher = true;
                break;
            }
        }
        return isMatcher;
    }

    /*过滤器优先级 越小越优先*/
    @Override
    public int getOrder() {
        /*最高级*/
        return Ordered.HIGHEST_PRECEDENCE;
    }
}
